from fill_in_error import __, assert_equal

# we mainly talk about function argument here

def add(a, b):
    return a + b

def add_with_default_argument_value(a, b = 10):
    return a + b

# *args means it can receive any number of arguments
def put_all_argument_into_a_list(*args):
    return [elem for elem in args]
    # actualy, args itself is list
    # so we are definig a stupid function

# **kwargs means any number of (key, value) pairs
def put_all_kv_into_a_dict(**kwargs):
    {(kw, kwargs[kw]) for kw in kwargs}
    # actually, kwargs itself is a dict, hahaha
    # so again we are defining a stupid function

class AboutFunction:
    def call_a_normal_function():
        assert_equal(add(2, 3), __)

    def call_a_function_with_default_value():
        assert_equal(add_with_default_argument_value(2, 3), __)
        assert_equal(add_with_default_argument_value(2), __)
        # we can call this function with explicit keyword "b="
        assert_equal(add_with_default_argument_value(2, b=3), __)
    
    def call_a_function_with_var_arguments():
        assert_equal(put_all_argument_into_a_list(1), __)
        assert_equal(put_all_argument_into_a_list(1, 2), __)
        assert_equal(put_all_argument_into_a_list(1, 2, "lalala"), __)
        # now you can understand how print() works
        
    def call_a_function_with_keyword_arguments():
        assert_equal(put_all_kv_into_a_dict(sam="handsome"), __)
        assert_equal(put_all_kv_into_a_dict(sam="handsome", ni="smart"), __)